package com.fy.fyspace.common.manager;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.model.ListObjectsV2Request;
import com.aliyun.oss.model.ListObjectsV2Result;
import com.fy.fyspace.common.config.CosClientConfig;
import com.fy.fyspace.common.utils.DateFormatter;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.model.*;
import com.qcloud.cos.model.ciModel.persistence.PicOperations;
import com.qcloud.cos.region.Region;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Component
public class CosManager {

    @Resource
    private CosClientConfig cosClientConfig;

    private COSClient defaultCosClient;

    private static final String FILE_PREFIX = "/";
    
    /**
     * 分片上传的默认分片大小 2MB
     */
    private static final long PART_SIZE = 2 * 1024 * 1024;

    /**
     * 用于存储分片上传的状态信息
     */
    private final Map<String, UploadStatus> uploadStatusMap = new ConcurrentHashMap<>();

    /**
     * 上传状态类
     */
    private static class UploadStatus {
        String uploadId;
        ConcurrentMap<Integer, String> partEtags = new ConcurrentHashMap<>();
        CountDownLatch uploadLatch;
        volatile boolean isCompleted = false;
        long lastUpdateTime;

        UploadStatus(String uploadId, int totalParts) {
            this.uploadId = uploadId;
            this.uploadLatch = new CountDownLatch(totalParts);
            this.lastUpdateTime = System.currentTimeMillis();
        }

        void addPart(int partNumber, String etag) {
            partEtags.put(partNumber, etag);
            uploadLatch.countDown();
            lastUpdateTime = System.currentTimeMillis();
        }

        boolean waitForCompletion(long timeout, TimeUnit unit) throws InterruptedException {
            return uploadLatch.await(timeout, unit);
        }
    }

    @PostConstruct
    public void init() {
        this.defaultCosClient = createCOSClient();
    }

    @PreDestroy
    public void destroy() {
        if (defaultCosClient != null) {
            defaultCosClient.shutdown();
        }
    }

    /**
     * 获取COS客户端配置信息
     */
    private String getBucketName() {
        return cosClientConfig.getBucket();
    }

    /**
     * 获取完整的对象URL
     */
    private String getObjectUrl(String objectName) {
        return String.format("https://%s.cos.%s.myqcloud.com/%s",
                getBucketName(),
                cosClientConfig.getRegion(),
                objectName);
    }

    /**
     * 创建COS客户端
     */
    private COSClient createCOSClient() {
        COSCredentials cred = new BasicCOSCredentials(cosClientConfig.getSecretId(), cosClientConfig.getSecretKey());
        ClientConfig clientConfig = new ClientConfig(new Region(cosClientConfig.getRegion()));
        return new COSClient(cred, clientConfig);
    }

    /**
     * 生成对象存储的Key
     */
    private String generateObjectKey(String fileName) {
        String timestamp = DateFormatter.getNowDateTimeString();
        String random = String.valueOf(Math.abs(new Random().nextInt()));
        String extension = fileName.substring(fileName.lastIndexOf("."));
        return FILE_PREFIX + timestamp + random + extension;
    }

    /**
     * 上传对象
     */
    public PutObjectResult putObject(String key, File file) {
        PutObjectRequest putObjectRequest = new PutObjectRequest(getBucketName(), key, file);
        // 添加图片处理参数
        PicOperations picOperations = new PicOperations();
        picOperations.setIsPicInfo(1); // 返回图片基本信息
        putObjectRequest.setPicOperations(picOperations);
        return defaultCosClient.putObject(putObjectRequest);
    }

    /**
     * 下载对象
     */
    public COSObject getObject(String key) {
        GetObjectRequest getObjectRequest = new GetObjectRequest(getBucketName(), key);
        return defaultCosClient.getObject(getObjectRequest);
    }

    /**
     * 删除对象
     */
    public void deleteObject(String key) {
        defaultCosClient.deleteObject(getBucketName(), key);
    }

    /**
     * 上传OCR图片
     */
    public String uploadOcrPicture(File file) {
        // 检查文件大小限制（5MB）
        if (file.length() > 5 * 1024 * 1024) {
            throw new IllegalArgumentException("图片大小不能超过5MB");
        }
        
        // 生成唯一文件名
        String suffix = FileUtil.getSuffix(file.getName());
        String key = "public/ocrPic/" + System.currentTimeMillis() + "_" + Math.round(Math.random() * 1000) + "." + suffix;
        
        try {
            // 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(getBucketName(), key, file);
            defaultCosClient.putObject(putObjectRequest);
            
            // 返回访问路径
            return getObjectUrl(key);
        } catch (Exception e) {
            log.error("OCR图片上传失败", e);
            throw new RuntimeException("图片上传失败", e);
        }
    }

    /**
     * 处理分片上传的完整流程
     * @param chunk 当前分片数据
     * @param fileName 文件名
     * @param fileSize 文件总大小
     * @param chunkIndex 当前分片索引
     * @param totalChunks 总分片数
     * @param uploadId 上传ID（首次上传为null）
     * @return 返回[uploadId, fileUrl]格式的数组，未完成返回[uploadId, null]，完成返回[null, fileUrl]
     */
    public List<String> handleMultipartUpload(byte[] chunk, String fileName, Long fileSize, 
                                                int chunkIndex, int totalChunks, String uploadId) {
        String objectKey = generateObjectKey(fileName);
        List<String> result = new ArrayList<>();
        
        try {
            // 1. 如果是第一个分片，初始化分片上传
            if (chunkIndex == 0) {
                InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(
                    cosClientConfig.getBucket(), objectKey);
                InitiateMultipartUploadResult initResult = defaultCosClient.initiateMultipartUpload(initRequest);
                uploadId = initResult.getUploadId();
                log.info("初始化分片上传 - 文件: {}, uploadId: {}, 总分片数: {}", 
                        fileName, uploadId, totalChunks);
            }

            // 2. 上传当前分片
            UploadPartRequest uploadRequest = new UploadPartRequest()
                .withBucketName(cosClientConfig.getBucket())
                .withKey(objectKey)
                .withUploadId(uploadId)
                .withPartNumber(chunkIndex + 1)
                .withInputStream(new ByteArrayInputStream(chunk))
                .withPartSize(chunk.length);

            UploadPartResult uploadResult = defaultCosClient.uploadPart(uploadRequest);
            String etag = uploadResult.getETag();
            
            // 3. 保存分片信息到状态Map
            String uploadKey = uploadId + "_" + objectKey;
            String finalUploadId = uploadId;
            UploadStatus status = uploadStatusMap.computeIfAbsent(uploadKey,
                k -> new UploadStatus(finalUploadId, totalChunks));
            status.addPart(chunkIndex + 1, etag);
            
            log.info("分片上传成功 - 文件: {}, 分片: {}/{}, ETag: {}", 
                    fileName, chunkIndex + 1, totalChunks, etag);

            // 4. 如果是最后一个分片，执行合并
            if (chunkIndex == totalChunks - 1) {
                // 验证所有分片是否都已上传
                if (status.partEtags.size() == totalChunks) {
                    // 按分片号排序
                    List<PartETag> partETags = status.partEtags.entrySet().stream()
                        .sorted(Map.Entry.comparingByKey())
                        .map(e -> new PartETag(e.getKey(), e.getValue()))
                        .collect(Collectors.toList());

                    // 完成分片上传
                    CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
                        cosClientConfig.getBucket(), objectKey, uploadId, partETags);
                    CompleteMultipartUploadResult completeResult = 
                        defaultCosClient.completeMultipartUpload(completeRequest);
                    
                    // 清理上传状态
                    uploadStatusMap.remove(uploadKey);
                    
                    log.info("分片上传完成 - 文件: {}, URL: {}", fileName, completeResult.getKey());
                    
                    result.add(null);
                    result.add(getObjectUrl(objectKey));
                    return result;
                } else {
                    log.error("文件分片不完整 - 文件: {}, 预期分片数: {}, 实际分片数: {}", 
                            fileName, totalChunks, status.partEtags.size());
                    throw new RuntimeException("文件分片不完整，请重新上传");
                }
            }

            // 5. 返回uploadId，继续上传后续分片
            result.add(uploadId);
            result.add(null);
            return result;

        } catch (Exception e) {
            log.error("分片上传异常 - 文件: {}, 分片: {}/{}, 错误: {}", 
                    fileName, chunkIndex + 1, totalChunks, e.getMessage());
            
            // 发生异常时，尝试中止上传
            if (uploadId != null) {
                try {
                    defaultCosClient.abortMultipartUpload(new AbortMultipartUploadRequest(
                        cosClientConfig.getBucket(), objectKey, uploadId));
                    uploadStatusMap.remove(uploadId + "_" + objectKey);
                } catch (Exception abortEx) {
                    log.error("中止上传失败 - 文件: {}, uploadId: {}", fileName, uploadId, abortEx);
                }
            }
            
            throw new RuntimeException("分片上传失败: " + e.getMessage());
        }
    }

    /**
     * 上传对象并获取图片主色调
     */
    public Map<String, String> putObjectAndGetColor(String key, File file) {
        Map<String, String> result = new HashMap<>();
        
        try {
            // 1. 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(getBucketName(), key, file);
            PutObjectResult putObjectResult = defaultCosClient.putObject(putObjectRequest);
            result.put("etag", putObjectResult.getETag());
            
            // 2. 等待文件上传完成后再获取主色调（添加短暂延时确保文件可访问）
            Thread.sleep(500);
            
            // 3. 使用imageAve接口获取图片主色调
            String imageAveUrl = String.format("https://%s.cos.%s.myqcloud.com/%s?imageAve",
                    getBucketName(),
                    cosClientConfig.getRegion(),
                    key);
                    
            GetObjectRequest getObjectRequest = new GetObjectRequest(getBucketName(), key + "?imageAve");
            COSObject cosObject = defaultCosClient.getObject(getObjectRequest);
            
            // 4. 读取响应内容
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(cosObject.getObjectContent()))) {
                String content = reader.lines().collect(Collectors.joining());
                JSONObject jsonObject = JSONUtil.parseObj(content);
                String rgb = jsonObject.getStr("RGB");
                // 只保留0xXXXXXX中的XXXXXX部分
                if (rgb != null && rgb.startsWith("0x")) {
                    rgb = rgb.substring(2);
                }
                result.put("mainColor", rgb);
                log.info("成功获取图片主色调 - key: {}, color: {}", key, rgb);
            }
            
            return result;
        } catch (Exception e) {
            log.error("上传文件或获取主色调失败 - key: {}, error: {}", key, e.getMessage());
            // 如果获取主色调失败，仍然返回上传结果，只是没有颜色信息
            return result;
        }
    }

    /**
     * 获取桶的统计信息（基础信息、用量、类型分布、分析等）
     */
    public Map<String, Object> getBucketStatistics() {
        Map<String, Object> result = new HashMap<>();
        try {
            // 1. 基础信息
            String bucketName = getBucketName();
            String region = cosClientConfig.getRegion();
            String endpoint = String.format("https://%s.cos.%s.myqcloud.com", bucketName, region);
            result.put("name", bucketName);
            result.put("region", region);
            result.put("endpoint", endpoint);

            // 获取ACL
            try {
                AccessControlList acl = defaultCosClient.getBucketAcl(bucketName);
                result.put("acl", acl.getGrantsAsList().toString());
            } catch (Exception e) {
                result.put("acl", "未知");
            }

            // 获取生命周期
            try {
                BucketLifecycleConfiguration lifecycle = defaultCosClient.getBucketLifecycleConfiguration(bucketName);
                result.put("lifecycle", lifecycle != null ? lifecycle.getRules().toString() : "未配置");
            } catch (Exception e) {
                result.put("lifecycle", "未配置");
            }

            // 获取版本控制
            try {
                BucketVersioningConfiguration versioning = defaultCosClient.getBucketVersioningConfiguration(bucketName);
                result.put("versioning", versioning != null ? versioning.getStatus() : "未开启");
            } catch (Exception e) {
                result.put("versioning", "未开启");
            }

            // 获取日志配置
            try {
                BucketLoggingConfiguration logging = defaultCosClient.getBucketLoggingConfiguration(bucketName);
                result.put("logging", logging != null && logging.isLoggingEnabled() ? "已开启" : "未开启");
            } catch (Exception e) {
                result.put("logging", "未开启");
            }

            // 2. 对象统计（腾讯云COS专用）
            long totalSize = 0, maxSize = 0, minSize = Long.MAX_VALUE, objectCount = 0;
            Map<String, Long> typeCount = new HashMap<>();
            Map<String, Long> typeSize = new HashMap<>();

            String nextMarker = null;
            do {
                ListObjectsRequest req = new ListObjectsRequest();
                req.setBucketName(bucketName);
                req.setMarker(nextMarker);
                req.setMaxKeys(1000);
                ObjectListing objectListing = defaultCosClient.listObjects(req);
                for (COSObjectSummary obj : objectListing.getObjectSummaries()) {
                    long size = obj.getSize();
                    totalSize += size;
                    maxSize = Math.max(maxSize, size);
                    minSize = Math.min(minSize, size);
                    objectCount++;
                    String ext = "other";
                    String key = obj.getKey();
                    int idx = key.lastIndexOf('.');
                    if (idx > 0 && idx < key.length() - 1) {
                        ext = key.substring(idx + 1).toLowerCase();
                    }
                    typeCount.merge(ext, 1L, Long::sum);
                    typeSize.merge(ext, size, Long::sum);
                }
                nextMarker = objectListing.getNextMarker();
            } while (nextMarker != null && !nextMarker.isEmpty());

            result.put("objectCount", objectCount);
            result.put("totalSize", totalSize);
            result.put("maxFileSize", maxSize);
            result.put("minFileSize", minSize == Long.MAX_VALUE ? 0 : minSize);
            result.put("avgFileSize", objectCount > 0 ? totalSize / objectCount : 0);
            // 类型分布
            Map<String, Object> typeDistribution = new HashMap<>();
            for (String ext : typeCount.keySet()) {
                Map<String, Object> info = new HashMap<>();
                info.put("count", typeCount.get(ext));
                info.put("size", typeSize.get(ext));
                typeDistribution.put(ext, info);
            }
            result.put("typeDistribution", typeDistribution);

            // 3. 分析
            long maxCapacity = 0; // 如有配额可配置
            double usagePercent = maxCapacity > 0 ? (double) totalSize / maxCapacity * 100 : 0;
            result.put("usagePercent", usagePercent);

        } catch (Exception e) {
            log.error("获取COS桶统计信息失败", e);
            result.put("error", e.getMessage());
        }
        return result;
    }
}

